﻿#region License

//
// Author: Javier Lozano <javier@lozanotek.com>
// Copyright (c) 2009-2010, lozanotek, inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#endregion

namespace MvcTurbine.Extensions {
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Extension methods for collections.
    /// </summary>
    public static class CollectionExtensions {
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="action"></param>
        public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action) {
            if (collection == null) {
                throw new ArgumentNullException("collection");
            }

            if (action == null) {
                throw new ArgumentNullException("action");
            }

            foreach (T item in collection) {
                action(item);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static bool TrueForAll<T>(this IEnumerable<T> collection, Predicate<T> match) {
            if (collection == null) {
                throw new ArgumentNullException("collection");
            }

            if (match == null) {
                throw new ArgumentNullException("match");
            }

            foreach (T item in collection) {
                if (!match(item)) return false;
            }

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="collection"></param>
        /// <param name="converter"></param>
        /// <returns></returns>
        public static IEnumerable<U> ConvertAll<T, U>(this IEnumerable<T> collection, Converter<T, U> converter) {
            if (collection == null) {
                throw new ArgumentNullException("collection");
            }

            if (converter == null) {
                throw new ArgumentNullException("converter");
            }

            foreach (T item in collection) {
                yield return converter(item);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection1"></param>
        /// <param name="collection2"></param>
        /// <returns></returns>
        public static IEnumerable<T> Complement<T>(this IEnumerable<T> collection1, IEnumerable<T> collection2) {
            foreach (T item in collection1) {
                if (collection2.Contains(item) == false) {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection1"></param>
        /// <param name="collection2"></param>
        /// <returns></returns>
        public static IEnumerable<T> Except<T>(IEnumerable<T> collection1, IEnumerable<T> collection2)
            where T : IEquatable<T> {
            IEnumerable<T> complement1 = Complement(collection1, collection2);
            IEnumerable<T> complement2 = Complement(collection2, collection1);
            return complement1.Union(complement2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static IEnumerable<T> Sort<T>(this IEnumerable<T> collection) {
            if (collection == null) {
                throw new ArgumentNullException("collection");
            }

            var list = new List<T>(collection);
            list.Sort();

            foreach (T item in list) {
                yield return item;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static T Find<T>(this IEnumerable<T> collection, Predicate<T> predicate) {
            foreach (T item in collection) {
                if (predicate(item)) return item;
            }

            return default(T);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int FindIndex<T>(this IEnumerable<T> collection, T value) where T : IEquatable<T> {
            if (collection == null) {
                throw new ArgumentNullException("collection");
            }

            using (IEnumerator<T> iterator = collection.GetEnumerator()) {
                int index = 0;

                while (iterator.MoveNext()) {
                    if (iterator.Current.Equals(value) == false) {
                        index++;
                    } else {
                        return index;
                    }
                }
                return -1;
            }
        }
    }
}
